package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.jdbc.Page;
import com.uinnova.product.eam.comm.model.es.EamCategory;
import com.uinnova.product.eam.model.cj.domain.PlanDesignInstance;
import com.uinnova.product.eam.model.enums.CategoryTypeEnum;
import com.uinnova.product.eam.service.FlushDataService;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.asset.BmConfigSvc;
import com.uinnova.product.eam.service.cj.dao.PlanDesignInstanceDao;
import com.uinnova.product.eam.service.es.EamCategoryDesignDao;
import com.uinnova.product.eam.service.es.EamCategoryPrivateDao;
import com.uinnova.product.vmdb.comm.model.ci.CCcCiClass;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.project.base.diagram.comm.model.ESDiagram;
import com.uinnova.project.db.eam.ESDiagramDao;
import com.uino.api.client.cmdb.ICIClassApiSvc;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESCISearchBean;
import lombok.Builder;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Log4j2
public class FlushDataServiceImpl implements FlushDataService {

    @Autowired
    private BmConfigSvc bmConfigSvc;
    @Autowired
    private ICIClassApiSvc classApiSvc;
    @Autowired
    private ICISwitchSvc ciSwitchSvc;
    @Autowired
    private EamCategoryPrivateDao categoryPrivateDao;
    @Autowired
    private EamCategoryDesignDao categoryDesignDao;
    @Autowired
    private PlanDesignInstanceDao planDesignInstanceDao;
    @Autowired
    private ESDiagramDao diagramDao;

    @Override
    public int systemDirDrop() {
        String confStr = bmConfigSvc.getConfigType("SYS_DIR_DROP");
        if (StringUtils.isBlank(confStr)) {
            throw new BinaryException("先配置必要信息");
        }
        JSONObject conf = JSON.parseObject(confStr);
        Context context = prepareContext(conf);
        Set<Long> dropDesignIds = new HashSet<>();
        if (!CollectionUtils.isEmpty(context.getDesignCategories())) {
            for (EamCategory category : context.getDesignCategories()) {
                Long id = category.getId();
                if (context.getDesignHasChildCategoryIds().contains(id)
                        || context.getDesignHasPlanIds().contains(id)
                        || context.getDesignHasDiagramIds().contains(id)) {
                    continue;
                }
                log.info("删除资产目录[{}],ciCode：{}", category.getDirName(), category.getCiCode());
                dropDesignIds.add(id);
            }
        }
        Set<Long> dropPrivateIds = new HashSet<>();
        if (!CollectionUtils.isEmpty(context.getPrivateCategories())) {
            for (EamCategory category : context.getPrivateCategories()) {
                Long id = category.getId();
                if (context.getPrivateHasChildCategoryIds().contains(id)
                        || context.getPrivateHasPlanIds().contains(id)
                        || context.getPrivateHasDiagramIds().contains(id)) {
                    continue;
                }
                log.info("删除私有库目录[{}]，ciCode：{}，所属人[{}]", category.getDirName(), category.getCiCode(), category.getOwnerCode());
                dropPrivateIds.add(id);
            }
        }
        if (!CollectionUtils.isEmpty(dropDesignIds)) {
            categoryDesignDao.deleteByIds(dropDesignIds);
        }
        if (!CollectionUtils.isEmpty(dropPrivateIds)) {
            categoryPrivateDao.deleteByIds(dropPrivateIds);
        }
        return Math.addExact(dropDesignIds.size(), dropPrivateIds.size());
    }

    private Context prepareContext(JSONObject conf) {
        String ingoreCiClassCode = conf.getString("ingoreCiClassCode");
        if (StringUtils.isBlank(ingoreCiClassCode)) {
            throw new BinaryException("先配置分类信息");
        }
        CCcCiClass cdt = new CCcCiClass();
        cdt.setClassCodeEqual(ingoreCiClassCode);
        List<CcCiClassInfo> classInfos = classApiSvc.queryClassByCdt(cdt);
        if (CollectionUtils.isEmpty(classInfos)) {
            throw new BinaryException("分类不存在：" + ingoreCiClassCode);
        }
        List<ESCIInfo> ingoreCiInfos = queryDesignCis(classInfos.get(0).getCiClass().getId());
        Set<String> ingorePrivateCiCodes = new HashSet<>();
        if (!CollectionUtils.isEmpty(ingoreCiInfos)) {
            ingorePrivateCiCodes = ingoreCiInfos.stream().map(ESCIInfo::getCiCode).collect(Collectors.toSet());
        }
        Set<String> ingoreDesignCiCodes = new HashSet<>();
        if (!CollectionUtils.isEmpty(ingorePrivateCiCodes)) {
            ingoreDesignCiCodes.addAll(ingorePrivateCiCodes);
        }
        if (!StringUtils.isBlank(conf.getString("ingoreDesignCiCodes"))) {
            List<String> confIngoreDesignCiCodes = JSON.parseArray(conf.getString("ingoreDesignCiCodes"), String.class);
            if (!CollectionUtils.isEmpty(confIngoreDesignCiCodes)) {
                ingoreDesignCiCodes.addAll(confIngoreDesignCiCodes);
            }
        }
        List<String> ingorePrivateUserLoginCodes = new ArrayList<>();
        if (!StringUtils.isBlank(conf.getString("ingorePrivateUserLoginCodes"))) {
            ingorePrivateUserLoginCodes = JSON.parseArray(conf.getString("ingorePrivateUserLoginCodes"), String.class);
        }
        //查资产库待删系统文件夹
        BoolQueryBuilder designCategoryQuery = QueryBuilders.boolQuery();
        designCategoryQuery.must(QueryBuilders.termQuery("type", CategoryTypeEnum.SYSTEM.val()));
        if (!CollectionUtils.isEmpty(ingoreDesignCiCodes)) {
            designCategoryQuery.mustNot(QueryBuilders.termsQuery("ciCode.keyword", ingoreDesignCiCodes));
        }
        List<EamCategory> designCategories = categoryDesignDao.getListByQueryScroll(designCategoryQuery);
        Set<Long> designHasChildCategoryIds = new HashSet<>();
        Set<Long> designHasPlanIds = new HashSet<>();
        Set<Long> designHasDiagramIds = new HashSet<>();
        if (!CollectionUtils.isEmpty(designCategories)) {
            Set<Long> designCategoryIds = designCategories.stream().map(EamCategory::getId).collect(Collectors.toSet());
            TermsQueryBuilder childCategoryQuery = QueryBuilders.termsQuery("parentId", designCategoryIds);
            List<EamCategory> childCategoies = categoryDesignDao.getListByQueryScroll(childCategoryQuery);
            if (!CollectionUtils.isEmpty(childCategoies)) {
                designHasChildCategoryIds = childCategoies.stream().map(EamCategory::getParentId).collect(Collectors.toSet());
            }

            BoolQueryBuilder childPlanQuery = QueryBuilders.boolQuery();
            childPlanQuery.must(QueryBuilders.termQuery("assetsType", 2));
            childPlanQuery.must(QueryBuilders.termsQuery("assetsDirId", designCategoryIds));
            List<PlanDesignInstance> plans = planDesignInstanceDao.getListByQueryScroll(childPlanQuery);
            if (!CollectionUtils.isEmpty(plans)) {
                designHasPlanIds = plans.stream().map(PlanDesignInstance::getAssetsDirId).collect(Collectors.toSet());
            }

            BoolQueryBuilder childDiagramQuery = QueryBuilders.boolQuery();
            childDiagramQuery.must(QueryBuilders.termQuery("dataStatus", 1));
            childDiagramQuery.must(QueryBuilders.termQuery("historyVersionFlag", 1));
            childDiagramQuery.must(QueryBuilders.termQuery("isOpen", 1));
            childDiagramQuery.must(QueryBuilders.termsQuery("dirId", designCategoryIds));
            List<ESDiagram> diagrams = diagramDao.getListByQueryScroll(childDiagramQuery);
            if (!CollectionUtils.isEmpty(diagrams)) {
                designHasDiagramIds = diagrams.stream().map(ESDiagram::getDirId).collect(Collectors.toSet());
            }
        }


        BoolQueryBuilder privateCategoryQuery = QueryBuilders.boolQuery();
        privateCategoryQuery.must(QueryBuilders.termQuery("type", CategoryTypeEnum.SYSTEM.val()));
        if (!CollectionUtils.isEmpty(ingorePrivateCiCodes)) {
            privateCategoryQuery.mustNot(QueryBuilders.termsQuery("ciCode.keyword", ingorePrivateCiCodes));
        }
        if (!CollectionUtils.isEmpty(ingorePrivateUserLoginCodes)) {
            privateCategoryQuery.mustNot(QueryBuilders.termsQuery("ownerCode.keyword", ingorePrivateUserLoginCodes));
        }
        List<EamCategory> privateCategories = categoryPrivateDao.getListByQueryScroll(privateCategoryQuery);
        Set<Long> privateHasChildCategoryIds = new HashSet<>();
        Set<Long> privateHasPlanIds = new HashSet<>();
        Set<Long> privateHasDiagramIds = new HashSet<>();
        if (!CollectionUtils.isEmpty(privateCategories)) {
            Set<Long> privateCategoryIds = privateCategories.stream().map(EamCategory::getId).collect(Collectors.toSet());
            TermsQueryBuilder query = QueryBuilders.termsQuery("parentId", privateCategoryIds);
            List<EamCategory> childCategoies = categoryPrivateDao.getListByQueryScroll(query);
            if (!CollectionUtils.isEmpty(childCategoies)) {
                privateHasChildCategoryIds = childCategoies.stream().map(EamCategory::getParentId).collect(Collectors.toSet());
            }

            BoolQueryBuilder childPlanQuery = QueryBuilders.boolQuery();
            childPlanQuery.must(QueryBuilders.termQuery("assetsType", 1));
            childPlanQuery.must(QueryBuilders.termsQuery("dirId", privateCategoryIds));
            List<PlanDesignInstance> plans = planDesignInstanceDao.getListByQueryScroll(childPlanQuery);
            if (!CollectionUtils.isEmpty(plans)) {
                privateHasPlanIds = plans.stream().map(PlanDesignInstance::getDirId).collect(Collectors.toSet());
            }

            BoolQueryBuilder childDiagramQuery = QueryBuilders.boolQuery();
            childDiagramQuery.must(QueryBuilders.termQuery("dataStatus", 1));
            childDiagramQuery.must(QueryBuilders.termQuery("historyVersionFlag", 1));
            childDiagramQuery.must(QueryBuilders.termQuery("isOpen", 0));
            childDiagramQuery.must(QueryBuilders.termsQuery("dirId", privateCategoryIds));
            List<ESDiagram> diagrams = diagramDao.getListByQueryScroll(childDiagramQuery);
            if (!CollectionUtils.isEmpty(diagrams)) {
                privateHasDiagramIds = diagrams.stream().map(ESDiagram::getDirId).collect(Collectors.toSet());
            }
        }
        return Context.builder()
                .designCategories(designCategories)
                .designHasChildCategoryIds(designHasChildCategoryIds)
                .designHasPlanIds(designHasPlanIds)
                .designHasDiagramIds(designHasDiagramIds)
                .privateCategories(privateCategories)
                .privateHasChildCategoryIds(privateHasChildCategoryIds)
                .privateHasPlanIds(privateHasPlanIds)
                .privateHasDiagramIds(privateHasDiagramIds)
                .build();
    }

    private List<ESCIInfo> queryDesignCis(Long ciClassId) {
        ESCISearchBean bean = new ESCISearchBean();
        bean.setClassIds(Arrays.asList(ciClassId));
        bean.setPageNum(1);
        bean.setPageSize(3000);
        Page<ESCIInfo> page = ciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN);
        List<ESCIInfo> allCi = new ArrayList<>(page.getData());
        if (page.getTotalRows() > 3000) {
            for (int i = 2; i < Math.ceil(page.getTotalRows() * 1.0 / 3000); i++) {
                bean.setPageNum(i);
                Page<ESCIInfo> pageByQuery = ciSwitchSvc.searchESCIByBean(bean, LibType.DESIGN);
                if (CollectionUtils.isEmpty(pageByQuery.getData())) {
                    break;
                }
                allCi.addAll(pageByQuery.getData());
            }
        }
        return allCi;
    }

    @Data
    @Builder
    private static class Context {
        private List<EamCategory> designCategories;
        private Set<Long> designHasChildCategoryIds;
        private Set<Long> designHasPlanIds;
        private Set<Long> designHasDiagramIds;

        private List<EamCategory> privateCategories;
        private Set<Long> privateHasChildCategoryIds;
        private Set<Long> privateHasPlanIds;
        private Set<Long> privateHasDiagramIds;
    }
}
